package com.chinalife.project.util;

import com.google.gson.*;
import org.apache.commons.lang3.StringEscapeUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @description gson工具类
 * @author synhard
 * @date 2024/7/11 17:18
 * @email songxinhang2023@e-chinalife.com
 * @id 18101119
 * @phone 13001321080
 */
@SuppressWarnings("all")
public class GsonUtil {

    /**
     * 不允许显式构造该类对象
     */
    private GsonUtil() {
        throw new UnsupportedOperationException("GsonUtil 禁止显式构造!");
    }

    /**
     * 获取全局单例的 gson 对象
     * 该对象是线程安全的, 不用每次手动构造, 优化性能
     *
     * @return
     */
    public static Gson getGson() {
        return GsonHolder.GSON;
    }

    /**
     * json字符串转为指定类型的对象
     * 由于Java泛型的擦除机制, 该方法用于目标对象类型不带泛型的对象
     * （如果对象不是泛型的, 只是字段是泛型的话这个方法是可以使用的）
     *
     * @param json
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T json2Obj(String json, Class<T> clazz) {
        return getGson().fromJson(json, clazz);
    }

    /**
     * 将json字符串转为指定类型的对象
     * 该方法用于带泛型类型的对象
     * 可以用下面的方式定义Type类型:
     * Type type = new TypeToken<T>(){}.getType();
     * 其中T为携带泛型的具体类型, 比如:
     * new TypeToken<List<UserInfoInfoBO>>(){}.getType()
     *
     * @param json
     * @param typeOfT
     * @param <T>
     * @return
     */
    public static <T> T json2Obj(String json, Type typeOfT) {
        return getGson().fromJson(json, typeOfT);
    }

    /**
     * 对象转换为json字符串
     *
     * @param object
     * @return
     */
    public static String obj2Json(Object object) {
        return getGson().toJson(object);
    }

    /**
     * 将对象转为 json 字符串, 方便参数传递
     * 会自动将双引号等特殊字符加上\转义符
     *
     * @param object
     * @return
     */
    public static String obj2JsonStr(Object object) {
        return stringify(obj2Json(object));
    }

    /**
     * 将一个普通的json格式字符串进行转义, 自动将双引号等特殊字符加上\转义符, 方便参数传递
     *
     * @param jsonStr
     * @return
     */
    public static String stringify(String jsonStr) {
        return StringEscapeUtils.escapeJson(jsonStr);
    }

    /**
     * gson对象的holder, 保证单例
     */
    private static class GsonHolder {
        private static String pattern = "yyyy-MM-dd HH:mm:ss";
        private static Gson GSON = null;

        static {
            GSON = new GsonBuilder()
                    .setDateFormat(pattern)
                    .disableHtmlEscaping()
                    .create();
        }
    }

    /**
     * @author synhard
     * @id 18101119
     * @description 将 JsonElement 转为 Java 对象，支持对于 JsonElement 类型的数据转为 String 类型，String 类型的转为实体类型
     * @date 2024/4/9 10:41
     * @param jsonObject JsonElement
     * @param clazz 要转为的实体类
     * @return T
     */
    public static <T> T parseJsonObject(JsonObject jsonObject, Class<T> clazz) {
        try {
            if (jsonObject.isJsonPrimitive()) {
                // 处理基本类型和String
                return getGson().fromJson(jsonObject, clazz);
            } else if (jsonObject.isJsonObject()) {
                T instance = clazz.getDeclaredConstructor().newInstance();

                for (Field field : clazz.getDeclaredFields()) {
                    field.setAccessible(true);
                    JsonElement element = jsonObject.get(field.getName());

                    if (element != null) {
                        if (element.isJsonObject() && field.getType().equals(String.class)) {
                            // 如果目标字段是String类型，则直接将JsonObject转换为String
                            String value = element.toString();
                            field.set(instance, value);
                        } else if (element.isJsonObject() || element.isJsonArray()) {
                            // 递归调用以处理嵌套的JsonObject或JsonArray
                            Object value = parseJsonObject((JsonObject) element, field.getType());
                            field.set(instance, value);
                        } else if (element.isJsonPrimitive()) {
                            Object value = getGson().fromJson(element, field.getType());
                            field.set(instance, value);
                        }
                    }
                }
                return instance;
            } else {
                throw new Exception("Unsupported JsonElement type");
            }
        } catch (Exception e) {
            throw new RuntimeException("parseJsonObject fail: jsonObject: " + jsonObject.toString() + "clazz: " + clazz.toString());
        }
    }

    /**
     * @author synhard
     * @id 18101119
     * @description 将 Java 对象 转为 JsonObject
     * @date 2024/4/18 16:58
     * @param javaBean 待转换的 Java 对象
     * @return com.google.gson.JsonObject
     */
    public static JsonObject parseJavaBean(Object javaBean) {
        JsonObject resultObject = getGson().toJsonTree(javaBean).getAsJsonObject();

        // 遍历JavaBean的所有属性
        Field[] fields = javaBean.getClass().getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                Object value = field.get(javaBean);

                // 检查属性是否是字符串并且是有效的JSON
                if (value instanceof String && isValidJson((String) value)) {
                    // 将JSON字符串转换为JsonElement
                    JsonElement jsonElement = getGson().fromJson((String) value, JsonElement.class);

                    // 替换原始的字符串属性
                    resultObject.add(field.getName(), jsonElement);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return resultObject;
    }

    /**
     * @author synhard
     * @id 18101119
     * @description 检查字符串是否合法
     * @date 2024/4/18 16:58
     * @param json json 格式字符串
     * @return boolean
     */
    private static boolean isValidJson(String json) {
        try {
            new Gson().fromJson(json, Object.class);
            return true;
        } catch (com.google.gson.JsonSyntaxException ex) {
            return false;
        }
    }

    /**
     * @description 从 JsonObject 中安全地获取另一个 JsonObject。
     * 如果指定的键不存在或其值为 JsonNull，则返回 null。
     * @author 18101119
     * @param jsonObject 要搜索的 JsonObject
     * @param key 要获取的键
     * @return 对应的 JsonObject 或 null
     */
    public static JsonObject getAsJsonObjectSafety(JsonObject jsonObject, String key) {
        if (jsonObject == null || !jsonObject.has(key) || jsonObject.get(key).isJsonNull()) {
            return null;
        }
        return jsonObject.getAsJsonObject(key);
    }

    /**
     * @description 从 JsonObject 中安全地获取 JsonArray。
     * 如果指定的键不存在或其值为 JsonNull，则返回空的 JsonArray。
     * @author 18101119
     * @param jsonObject 要搜索的 JsonObject
     * @param key 要获取的键
     * @return 对应的 JsonArray，如果不适用则为 empty JsonArray
     */
    public static JsonArray getAsJsonArraySafety(JsonObject jsonObject, String key) {
        if (jsonObject == null || !jsonObject.has(key) || jsonObject.get(key).isJsonNull()) {
            return null;
        }
        return jsonObject.getAsJsonArray(key);
    }

    /**
     * @param str
     * @return com.google.gson.JsonElement
     * @author 18101119
     * @description 将 String 类型的 str 转为 JsonElement 对象，若 str 为空串或 null 返回 null
     * @date 2024/3/15 16:05
     */
    public static JsonElement parseStringToJsonElement(String str) {
        if (str == null || str.trim().isEmpty()) {
            return null;
        }
        try {
            return JsonParser.parseString(str);
        } catch (Exception e) {
            throw new RuntimeException("Error parsing Json: " + str);
        }
    }

    /**
     * @param str
     * @return com.google.gson.JsonArray
     * @author 18101119
     * @description 将 String 类型的 str 转为 JsonArray 对象，若 str 为空串或 null 返回 null
     * @date 2024/3/15 16:06
     */
    public static JsonArray parseStringToJsonArray(String str) {
        JsonElement element = parseStringToJsonElement(str);
        if (element == null) {
            return null;
        }
        if (!element.isJsonArray()) {
            throw new RuntimeException("Error parsing Json to JsonArray: " + str);
        }
        return element.getAsJsonArray();
    }

    /**
     * @param str
     * @return com.google.gson.JsonObject
     * @author 18101119
     * @description 将 String 类型的 str 转为 JsonObject 对象，若 str 为空串或 null 返回 null
     * @date 2024/3/15 16:06
     */
    public static JsonObject parseStringToJsonObject(String str) {
        JsonElement element = parseStringToJsonElement(str);
        if (element == null) {
            return null;
        }
        if (!element.isJsonObject()) {
            throw new RuntimeException("Error parsing Json to JsonObject: " + str);
        }
        return element.getAsJsonObject();
    }

    /**
     * @param str
     * @return com.google.gson.JsonPrimitive
     * @author 18101119
     * @description 将 String 类型的 str 转为 JsonPrimitive 对象，若 str 为空串或 null 返回 null
     * @date 2024/3/15 16:07
     */
    public static JsonPrimitive parseStringToJsonPrimitive(String str) {
        JsonElement element = parseStringToJsonElement(str);
        if (element == null) {
            return null;
        }
        if (!element.isJsonPrimitive()) {
            throw new RuntimeException("Error parsing Json to JsonPrimitive: " + str);
        }
        return element.getAsJsonPrimitive();
    }

    /**
     * @param jsonObject
     * @param key
     * @return com.google.gson.JsonElement
     * @author 18101119
     * @description 公共方法
     * @date 2024/3/8 14:18
     */
    private static JsonElement get(JsonObject jsonObject, String key) {
        if (jsonObject.has(key)) {
            return jsonObject.get(key);
        }
        return null;
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.lang.String
     * @author 18101119
     * @description 根据 key 获取 String 类型对象，key 不存在则返回 null
     * @date 2024/3/8 14:18
     */
    public static String getAsString(JsonObject jsonObject, String key) {
        JsonElement targetJsonElemet = get(jsonObject, key);
        if (targetJsonElemet == null) {
            return null;
        }
        if (targetJsonElemet.isJsonPrimitive()) {
            return targetJsonElemet.getAsString();
        }
        throw new RuntimeException("获取属性失败！类型与属性类型不匹配");
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.lang.Boolean
     * @author 18101119
     * @description 根据 key 获取 Boolean 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:43
     */
    public static Boolean getAsBoolean(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsBoolean();
        } catch (UnsupportedOperationException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to Boolean.");
        }
    }

    /**
     * @param jsonObject
     * @param key
     * @return boolean
     * @author 18101119
     * @description 根据 key 获取 boolean 值，key 不存在则返回 false
     * @date 2024/3/12 14:43
     */
    public static boolean getAsBooleanValue(JsonObject jsonObject, String key) {
        Boolean booleanVal = getAsBoolean(jsonObject, key);
        return booleanVal != null && booleanVal;
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.lang.Byte
     * @author 18101119
     * @description 根据 key 获取 Byte 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:44
     */
    public static Byte getAsByte(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsByte();
        } catch (NumberFormatException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to Byte.");
        }
    }

    /**
     * @param jsonObject
     * @param key
     * @return byte
     * @author 18101119
     * @description 根据 key 获取 byte 值，key 不存在则返回 0
     * @date 2024/3/12 14:45
     */
    public static byte getAsByteValue(JsonObject jsonObject, String key) {
        Byte byteVal = getAsByte(jsonObject, key);
        return byteVal == null ? 0 : byteVal;
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.lang.Short
     * @author 18101119
     * @description 根据 key 获取 Short 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:46
     */
    public static Short getAsShort(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsShort();
        } catch (NumberFormatException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to Short.");
        }
    }

    /**
     * @param jsonObject
     * @param key
     * @return short
     * @author 18101119
     * @description 根据 key 获取 short 值，key 不存在则返回 0
     * @date 2024/3/12 14:46
     */
    public static short getAsShortValue(JsonObject jsonObject, String key) {
        Short shortVal = getAsShort(jsonObject, key);
        return shortVal == null ? 0 : shortVal;
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.lang.Integer
     * @author 18101119
     * @description 根据 key 获取 Integer 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:48
     */
    public static Integer getAsInteger(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsInt();
        } catch (NumberFormatException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to Integer.");
        }
    }

    /**
     * @param jsonObject
     * @param key
     * @return int
     * @author 18101119
     * @description 根据 key 获取 int 值，key 不存在则返回 0
     * @date 2024/3/12 14:49
     */
    public static int getAsIntValue(JsonObject jsonObject, String key) {
        Integer intVal = getAsInteger(jsonObject, key);
        return intVal == null ? 0 : intVal;
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.lang.Long
     * @author 18101119
     * @description 根据 key 获取 Long 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:50
     */
    public static Long getAsLong(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsLong();
        } catch (NumberFormatException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to Long.");
        }
    }

    /**
     * @param jsonObject
     * @param key
     * @return long
     * @author 18101119
     * @description 根据 key 获取 long 值，key 不存在则返回 0
     * @date 2024/3/12 14:50
     */
    public static long getAsLongValue(JsonObject jsonObject, String key) {
        Long longVal = getAsLong(jsonObject, key);
        return longVal == null ? 0L : longVal;
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.lang.Float
     * @author 18101119
     * @description 根据 key 获取 Float 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:51
     */
    public static Float getAsFloat(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsFloat();
        } catch (NumberFormatException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to Float.");
        }
    }

    /**
     * @param jsonObject
     * @param key
     * @return float
     * @author 18101119
     * @description 根据 key 获取 float 值，key 不存在则返回 0
     * @date 2024/3/12 14:51
     */
    public static float getAsFloatValue(JsonObject jsonObject, String key) {
        Float floatVal = getAsFloat(jsonObject, key);
        return floatVal == null ? 0.0F : floatVal;
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.lang.Double
     * @author 18101119
     * @description 根据 key 获取 Double 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:52
     */
    public static Double getAsDouble(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsDouble();
        } catch (NumberFormatException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to Double.");
        }
    }

    /**
     * @param jsonObject
     * @param key
     * @return double
     * @author 18101119
     * @description 根据 key 获取 double 值，key 不存在则返回 0
     * @date 2024/3/12 14:52
     */
    public static double getAsDoubleValue(JsonObject jsonObject, String key) {
        Double doubleVal = getAsDouble(jsonObject, key);
        return doubleVal == null ? 0.0D : doubleVal;
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.math.BigInteger
     * @author 18101119
     * @description 根据 key 获取 BigInteger 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:54
     */
    public static BigInteger getAsBigInteger(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsBigInteger();
        } catch (NumberFormatException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to BigInteger.");
        }
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.math.BigDecimal
     * @author 18101119
     * @description 根据 key 获取 BigDecimal 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:54
     */
    public static BigDecimal getAsBigDecimal(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsBigDecimal();
        } catch (NumberFormatException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to BigDecimal.");
        }
    }

    /**
     * @param jsonObject
     * @param key
     * @return java.lang.Number
     * @author 18101119
     * @description 根据 key 获取 Number 类型对象，key 不存在则返回 null
     * @date 2024/3/12 14:54
     */
    public static Number getAsNumber(JsonObject jsonObject, String key) {
        JsonElement element = jsonObject.get(key);
        if (element == null || element.isJsonNull()) {
            return null;
        }
        try {
            return element.getAsNumber();
        } catch (NumberFormatException e) {
            throw new RuntimeException("Cannot convert key [" + key + "] to Number.");
        }
    }

    /**
     * 根据 key 获取 Date 类型的 value
     *
     * @param jsonObject
     * @param key
     * @param dateFormat 日期格式
     * @return
     */
    public static Date getAsDate(JsonObject jsonObject, String key, String dateFormat) {
        JsonElement targetJsonElemet = get(jsonObject, key);
        if (targetJsonElemet == null) {
            return null;
        }
        if (targetJsonElemet.isJsonPrimitive()) {
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            try {
                return sdf.parse(targetJsonElemet.getAsString());
            } catch (ParseException e) {
                throw new RuntimeException("获取属性失败！类型与属性类型不匹配");
            }
        }
        throw new RuntimeException("获取属性失败！类型与属性类型不匹配");
    }
}
